/**
 * Author(s): xuming.Wong <xuming.Wong@gmail.com>
 */

#ifndef __SUB_MESH_BUFFER_H__
#define __SUB_MESH_BUFFER_H__

#include "OgreSubMesh.h"
#include "OgreMesh.h"

#include "BlenderMeshUtil.h"
#include "MeshVertex.h"
#include "MeshFace.h"

#include "blender.h"

namespace GNgine
{
	//according to material divide a mesh into submesh
	//faces that same material belong to a same submesh if the mesh have material
	class SubMeshSlot
	{
	public:
		SubMeshSlot();
		SubMeshSlot(const SubMeshSlot &submesh);
		const SubMeshSlot& operator= (const SubMeshSlot& submesh);

		bool operator == (const SubMeshSlot& submesh) const;

		short material_nr, mode, alpha;
		Blender::Image *tpage[8];
	};

	
	inline SubMeshSlot::SubMeshSlot()
	{
		alpha = material_nr= mode= 0;
		tpage[0]= 0;
		tpage[1]= 0;
		tpage[2]= 0;
		tpage[3]= 0;
		tpage[4]= 0;
		tpage[5]= 0;
		tpage[6]= 0;
		tpage[7]= 0;
	}

	
	inline SubMeshSlot::SubMeshSlot(const SubMeshSlot &submesh)
	{
		*this= submesh;
	}

	inline bool SubMeshSlot::operator == (const SubMeshSlot& submesh) const
	{
		return ( mode  == submesh.mode && alpha == submesh.alpha && 
				 tpage[0] == submesh.tpage[0] &&
				 tpage[1] == submesh.tpage[1] &&
				 tpage[2] == submesh.tpage[2] &&
				 tpage[3] == submesh.tpage[3] &&
				 tpage[4] == submesh.tpage[4] &&
				 tpage[5] == submesh.tpage[5] &&
				 tpage[6] == submesh.tpage[6] &&
				 tpage[7] == submesh.tpage[7]);

	}

	
	inline const SubMeshSlot& SubMeshSlot::operator= (const SubMeshSlot& submesh)
	{
		material_nr= submesh.material_nr;
		mode= submesh.mode;
		alpha = submesh.alpha;
		tpage[0]= submesh.tpage[0];
		tpage[1]= submesh.tpage[1];
		tpage[2]= submesh.tpage[2];
		tpage[3]= submesh.tpage[3];
		tpage[4]= submesh.tpage[4];
		tpage[5]= submesh.tpage[5];
		tpage[6]= submesh.tpage[6];
		tpage[7]= submesh.tpage[7];
		return *this;
	}
	
	class SubMeshItem
	{
	public:
		typedef std::vector<MeshVertex> VertexBuffer;
		typedef std::vector<MeshFaceIndex> IndexBuffer;
		typedef std::map<int, int> IndexMap;
		//a fast solution is use a integer array,
		//where array subscript is index,and array element is value
		//int indexmap[];
		// The ogre submesh
		Ogre::SubMesh* submesh;

		VertexBuffer vbuf;
		IndexBuffer  ibuf;
		IndexMap indexmap;
	public:

		SubMeshItem();
		~SubMeshItem();

		int addVertex(int index, const MeshVertex& vertex);
		void addTri(const MeshFace& face);
	};
	
	struct SubMeshItemPair
	{
		SubMeshSlot *slot;
		SubMeshItem *item;

		inline SubMeshItemPair(SubMeshSlot *s=0,SubMeshItem *i=0)
		{
			slot=s;
			item=i;
		}
	};
	
	class SubMeshBufferLookup
	{
	private:
		typedef std::vector<SubMeshItemPair> List;
		List _list;
	public:
		SubMeshBufferLookup()
		{
			_list.reserve(15);
		}

		inline SubMeshItem* get(SubMeshSlot& slot)
		{
			SubMeshItem* item=0;
			int size=_list.size();
			for(int i=0;i<size;i++){
				if(slot==*(_list[i].slot)){
					item=_list[i].item;
					return item;
				}
			}
			return 0;
		}

		inline void clear()
		{
			int size=_list.size();
			for(int i=0;i<size;i++){
				if(_list[i].slot!=0){
					delete _list[i].slot;
				}
				if(_list[i].item!=0){
					delete _list[i].item;
				}
			}
		}

		inline int size()
		{
			return _list.size();
		}


		inline SubMeshItem* getItem(int index)
		{
			int size=_list.size();
			if(index>=0&&index<size){
				return _list[index].item;
			}
			return 0;
		}

		inline SubMeshSlot* getSlot(int index)
		{
			int size=_list.size();
			if(index>=0&&index<size){
				return _list[index].slot;
			}
			return 0;
		}

		//slot: is used for identify which submesh group the new submesh is belong to
		//mesh:the whole mesh which the new submesh is belong to
		//faces: face count of the submesh
		//vertices: vertices count of the submesh
		inline SubMeshItem* add(SubMeshSlot& slot,Ogre::Mesh *mesh,int faces,int vertices)
		{
			SubMeshItem* item=get(slot);
			if (item == 0){
				item= new SubMeshItem();
				item->submesh= mesh->createSubMesh();

				item->ibuf.reserve(faces);
				item->vbuf.reserve(vertices);

				SubMeshItemPair p(new SubMeshSlot(slot),item);
				_list.push_back(p);
			}
			return item;
		}

	};

	inline SubMeshItem::SubMeshItem()
		: submesh(0)
	{
	}

	
	inline SubMeshItem::~SubMeshItem()
	{
	}

	
	inline int SubMeshItem::addVertex(int index, const MeshVertex& vertex)
	{
		int found=-1;
		int size=vbuf.size();
		//if vertex already in vertex buffer just return index in vertex buffer
		IndexMap::iterator it=indexmap.find(index);
		if(it!=indexmap.end()){
			int second=it->second;
			if(second<size&&vbuf[second]==vertex){
				found=second;
				return found;
			}
		}

		//if vertex not in vertex buffer,
		//insert it in vertex buffer
		//and return the newly created vertext buffer position in vertex buffer
		
		if(found==-1){
			vbuf.push_back(vertex);
			indexmap.insert(IndexMap::value_type(index,size));
			return size;
		}

		return found;
	}
	
	inline void SubMeshItem::addTri(const MeshFace& face)
	{
		MeshFaceIndex item;
		item.v1= addVertex(face.v0.index, face.v0);
		item.v2= addVertex(face.v1.index, face.v1);
		item.v3= addVertex(face.v2.index, face.v2);
		ibuf.push_back(item);
	}

}

#endif
